iT邦幫忙

2024 iThome 鐵人賽

DAY 21
0

代理模式透過包裝物件來管理外部對物件的操作行為。

生活案例

每名偶像都有一位經紀人,負責管理其工作行程。廠商會透過經紀人洽談合作機會,並透過經紀人取得回覆。廠商不會直接向偶像發送邀約,偶像也不需直接回應廠商。經紀人代表偶像接收需求並做出回應,這就像代理模式中,代理人替目標對象管理需求一樣。

舉個例子

身為一名前端工程師,你應該對響應式變數這個詞十分熟悉。現代的前端框架經常使用響應式系統來處理畫面更新。響應式系統的核心特色在於自動更新,每當響應式變數發生變化,系統就會自動重新計算並更新畫面。透過響應式系統,工程師無需手動操作和更新畫面,大幅提升了開發效率和程式的維護性。

響應式系統有很多實踐方式,其中不乏代理模式的應用,像是 Vue 的 Reactivity API 就是一個經典的例子。讓我們看看,如何透過代理模式來模擬響應式系統的自動更新機制。

這個類別負責扮演變數的代理人。它將變數封裝為私有屬性,並透過 getter 和 setter 來管理存取行為。當變數被修改時,它會自動通知所有訂閱的元件,確保每個元件能即時更新。

class Reactive<T = any> {
  private _value: T;
  private components: Set<Component>;

  constructor(initialValue: T) {
    this._value = initialValue;
    this.components = new Set();
  }

  get value() {
    return this._value;
  }

  set value(newValue: T) {
    this._value = newValue;
    this.notifyComponents();
  }

  subscribe(component: Component) {
    this.components.add(component);
  }

  unsubscribe(component: Component) {
    this.components.delete(component);
  }

  notifyComponents() {
    this.components.forEach((component) => {
      component.update(this._value);
    });
  }
}

這個類別負責建立 UI 元件。元件會在初始化時訂閱指定的響應式變數,並在每次資料更新時自動重新渲染。

class Component<T = any> {
  private name: string;
  private data: T;

  constructor(name: string, reactive: Reactive<T>) {
    this.name = name;
    this.data = reactive.value;
    reactive.subscribe(this);
  }

  render() {
    console.log(`${this.name} component rendered with data:`, this.data);
  }

  update(newValue: T) {
    if (!Object.is(this.data, newValue)) {
      this.data = newValue;
      this.render();
    }
  }
}

透過簡單的代辦清單和項目加總來測試響應式系統。我們定義了待辦清單和項目加總的響應式變數,綁定並渲染對應的 UI,然侯模擬資料更新來觸發重新渲染。

class ReactiveTestDrive {
  static main() {
    const todo = new Reactive(["Go grocery shopping"]);
    const counter = new Reactive(todo.value.length);

    const todoList = new Component("TodoList", todo);
    const todoCounter = new Component("TodoCounter", counter);

    todoList.render();
    todoCounter.render();

    todo.value = [...todo.value, "Do the laundry"];
    counter.value = todo.value.length;
    todo.value = [...todo.value, "Clean the house"];
    counter.value = todo.value.length;
  }
}

ReactiveTestDrive.main();

執行結果。

TodoList component rendered with data: [ 'Go grocery shopping' ]
TodoCounter component rendered with data: 1
TodoList component rendered with data: [ 'Go grocery shopping', 'Do the laundry' ]
TodoCounter component rendered with data: 2
TodoList component rendered with data: [ 'Go grocery shopping', 'Do the laundry', 'Clean the house' ]
TodoCounter component rendered with data: 3

定義

Proxy Pattern

  • 對象介面(Subject): 實際對象的介面
  • 代理人(Proxy): 代替實際對象與外界溝通的物件
  • 實際對象(RealSubject): 真正執行請求的對象

代理人是客戶端程式和實際對象之間的溝通橋樑。它負責協調兩者之間的互動行為,所有向實體物件發出的請求都必須通過它來實現。代理人可以管理客戶端對實際對象的操作行為,並藉由攔截請求來執行額外的邏輯,像是驗證資料或紀錄事件。代理模式能夠輕鬆擴充實際對象的功能,並保持原有行為的獨立性與完整性,讓我們更為靈活地設計程式。

總結

  • 代理人是客戶端程式與實際對象之間的溝通橋樑
  • 代理人能夠攔截、檢查或擴展客戶端對實際對象的操作行為

完整範例

https://github.com/chengen0612/design-patterns-typescript/blob/main/patterns/structural/proxy.ts


上一篇
Day 20 - Null Object 空物件
下一篇
Day 22 - Mediator 中介者
系列文
前端也想學設計模式30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言